package com.tools.web.filter.gateway;

import com.tools.common.container.RowKit;
import com.tools.common.io.CharsetEnum;
import com.tools.common.io.ProfileKit;
import com.tools.common.object.Note;
import com.tools.common.object.string.VerifyKit;
import com.tools.common.security.CIDRPrefixTree;
import com.tools.common.security.SysKit;
import com.tools.common.thread.TimePair;
import com.tools.web.WebKit;
import com.tools.web.filter.cors.CorsFilter;
import com.tools.web.filter.cors.CorsSource;
import com.tools.web.interceptor.auth.AuthenticationManager;
import org.slf4j.Logger;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * 网关过滤器的抽象实现
 * */
@Note("网关过滤器的抽象实现")
public abstract class AbsGatewayManager implements GatewayManager {

    @Note("response 的 ContentType。默认为 JSON 字符串")
    private final String responseContentType;

    @Note("response 的默认字符集。默认为 UTF-8")
    private final String responseCharset;

    @Note("【能直连的 IP 白名单】 的 key。不为空则开启")
    private final String proxyWhitesCacheKey;

    @Note("【接口禁用列表】 的 key。不为空则开启")
    private final String disableURICacheKey;

    @Note("【客户端 IP 白名单】 - 的 key。不为空则开启")
    private final String ipWhitesCacheKey;

    @Note("【客户端 IP 黑名单】 - 的 key。不为空则开启")
    private final String ipBlacksCacheKey;

    @Note("是否开启了动态添加黑名单的功能")
    private boolean enabledAddBlack;

    @Note("【条件1】：不能为空。缓存的 key 的前缀。")
    private String addBlackerSaveKeyPrefix;

    @Note("【条件2】：大于 0。在该时间范围内")
    private long addBlackerThresholdTime;

    @Note("【条件3】：不能为 null。访问次数大于等于该阈值就拉黑")
    private int addBlackerThreshold;

    @Note("若选择在网关开启跨域设置，此处就是集成的跨域过滤器")
    private CorsFilter corsFilter;

    @Note("用户身份认证拦截器，若不为 null 则在封禁客户端 IP 时检测是否要封禁目标用户（目标用户已经登录的情况下）")
    private final AuthenticationManager authenticationManager;

    @Note("服务器上下文路径")
    private String serverContextPath;

    @Note("可直连的 IP 白名单")
    private CIDRPrefixTree proxyWhites;

    @Note("客户端 IP 访问白名单")
    private CIDRPrefixTree ipWhites;

    @Note("客户端 IP 访问黑名单")
    private CIDRPrefixTree ipBlacks;
    
    @Note("允许所有主机直连应用程序")
    private boolean allowAllOfProxyWhites;

    @Note("允许所有客户端 IP 访问")
    private boolean allowAllOfIpWhites;

    @Note("拒绝所有客户端 IP 访问")
    private boolean denyAllOfIpBlacks;

    /* ********************************************************************************************
     *
     *         构造器
     *
     * *********************************************************************************************
     * */

    public AbsGatewayManager(GatewaySource source) {
        this(source, null);
    }

    public AbsGatewayManager(GatewaySource source, AuthenticationManager authenticationManager) {
        if(source == null) throw new NullPointerException("GatewaySource 实例不能为 null");
        Logger logger = this.protected_getLogger();
        this.responseContentType = source.getResponseContentType();
        this.responseCharset = source.getResponseCharset().getValue();
        this.proxyWhitesCacheKey = source.getProxyWhitesCacheKey();
        this.private_initProxyWhites(logger, source);
        this.disableURICacheKey = source.getDisableURICacheKey();
        this.private_initDisabledURIs(logger, source);
        this.ipWhitesCacheKey = source.getIpWhitesCacheKey();
        this.private_initIpWhites(logger, source);
        this.ipBlacksCacheKey = source.getIpBlacksCacheKey();
        this.private_initIpBlacks(logger, source);
        AddBlacker addBlacker = source.getAddBlacker();
        if(addBlacker != null && addBlacker.isEnabled()) {
            this.addBlackerSaveKeyPrefix = addBlacker.getSaveKeyPrefix();
            this.addBlackerThresholdTime = this.protected_toSubclassDomainUnitTime(addBlacker.getThresholdTime());
            this.addBlackerThreshold = addBlacker.getThreshold();
            this.enabledAddBlack = true;
        }
        CorsSource corsSource = source.getCorsSource();
        if(corsSource != null) {
            this.corsFilter = new CorsFilter(corsSource);
            private_printEnabledLog(logger, this.corsFilter,
                    "开启 “跨域配置” 功能，将跨域相关的配置集成到此网关过滤器中");
        }
        this.authenticationManager = authenticationManager;
        private_printEnabledLog(logger, this.authenticationManager,
                "开启 “封禁账户” 功能，在封禁客户端 IP 时同步封禁用户账户");
    }

    /* ********************************************************************************************
     *
     *         提供给子类 / 需要子类实现的方法
     *
     * *********************************************************************************************
     * */

    @Note("获取子类的日志实例")
    protected abstract Logger protected_getLogger();

    @Note("将拉黑时间阈值范围转为子类自己所需的时间单位的时间值")
    protected abstract long protected_toSubclassDomainUnitTime(TimePair addBlackerThresholdTime);

    @Note("判断是否需要将该客户端 IP 加入 “客户端 IP 访问黑名单” 内")
    protected abstract String protected_checkIfAddIPInBlacksAndGetTimeunitLabel(String clientIP);

    @Note("初始化 REST 接口禁用列表")
    protected abstract int protected_initDisabledURIs(Set<String> disabledURIs);

    @Note("直接判断，省略 enabledDisabledURIs 的步骤")
    protected abstract boolean protected_isInDisabledURIs(String uri);

    @Note("直接设置子类的 disabledAllURI 属性")
    protected abstract void protected_setDisabledAllURIProperty(boolean all);

    @Note("获取判断访问次数记录的 Key")
    protected final String protected_getAddBlackSaveKey(String clientIP) {
        return this.addBlackerSaveKeyPrefix + clientIP;
    }

    public final long getAddBlackerThresholdTime() {
        return addBlackerThresholdTime;
    }

    public final int getAddBlackerThreshold() {
        return addBlackerThreshold;
    }

    public final String getAddBlackerSaveKeyPrefix() {
        return addBlackerSaveKeyPrefix;
    }

    @Override
    public final AuthenticationManager getAuthenticationManager() {
        return authenticationManager;
    }

    @Override
    public final CorsFilter getCorsFilter() {
        return corsFilter;
    }

    @Override
    public final String getResponseContentType() {
        return responseContentType;
    }

    @Override
    public final String getResponseCharset() {
        return responseCharset;
    }

    @Override
    public final CharsetEnum getResponseCharsetEnum() {
        return CharsetEnum.getEnum(responseCharset);
    }

    @Override
    public final String getProxyWhitesCacheKey() {
        return proxyWhitesCacheKey;
    }

    @Override
    public final String getDisableURICacheKey() {
        return disableURICacheKey;
    }

    @Override
    public final String getIpWhitesCacheKey() {
        return ipWhitesCacheKey;
    }

    @Override
    public final String getIpBlacksCacheKey() {
        return ipBlacksCacheKey;
    }

    public final String getServerContextPath() {
        return serverContextPath;
    }

    @Override
    public final boolean enabledProxyWhites() {
        return this.proxyWhitesCacheKey != null && !this.proxyWhitesCacheKey.isEmpty();
    }

    @Override
    public final boolean enabledDisabledURIs() {
        return this.disableURICacheKey != null && !this.disableURICacheKey.isEmpty();
    }

    @Override
    public final boolean enabledIpWhites() {
        return this.ipWhitesCacheKey != null && !this.ipWhitesCacheKey.isEmpty();
    }

    @Override
    public final boolean enabledIpBlacks() {
        return this.ipBlacksCacheKey != null && !this.ipBlacksCacheKey.isEmpty();
    }

    /* ********************************************************************************************
     *
     *         有关 IP 的增删查方法
     *
     * *********************************************************************************************
     * */
    
    @Override
    public AbsGatewayManager addInProxyWhites(String remote) {
        if(this.enabledProxyWhites()) {
            if("*".equals(remote)) this.allowAllOfProxyWhites = true;
            else this.proxyWhites.insert(remote);
        }
        return this;
    }

    @Override
    public AbsGatewayManager addAllInProxyWhites(String... remotes) {
        if(this.enabledProxyWhites()) {
            if(protected_changeAll(true, remotes) != null) this.allowAllOfProxyWhites = true;
            this.proxyWhites.insertBatch(remotes);
        }
        return this;
    }

    @Override
    public AbsGatewayManager addAllInProxyWhites(Iterable<String> remotes) {
        if(this.enabledProxyWhites())  {
            if(protected_changeAll(true, remotes) != null) this.allowAllOfProxyWhites = true;
            this.proxyWhites.insertBatch(remotes);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeFromProxyWhites(String remote) {
        if(this.enabledProxyWhites()) {
            if("*".equals(remote)) this.allowAllOfProxyWhites = false;
            else this.proxyWhites.remove(remote);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeAllFromProxyWhites(String... remotes) {
        if(this.enabledProxyWhites()) {
            if(protected_changeAll(false, remotes) != null) this.allowAllOfProxyWhites = false;
            this.proxyWhites.removeAll(remotes);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeAllFromProxyWhites(Iterable<String> remotes) {
        if(this.enabledProxyWhites()) {
            if(protected_changeAll(false, remotes) != null) this.allowAllOfProxyWhites = false;
            this.proxyWhites.removeAll(remotes);
        }
        return this;
    }

    @Override
    public AbsGatewayManager clearProxyWhites() {
        if(this.enabledProxyWhites()) {
            this.proxyWhites.clear();
            this.allowAllOfProxyWhites = false;
        }
        return this;
    }

    @Override
    public final boolean isInProxyWhites(String remote) {
        if(this.enabledProxyWhites()) {
            return private_checkIpIsIn(remote, this.allowAllOfProxyWhites, this.proxyWhites);
        }
        return true;
    }

    @Override
    public final boolean isAllInProxyWhites(String... remotes) {
        if(this.enabledProxyWhites()) {
            return private_checkIpIsAllIn(remotes, this.allowAllOfProxyWhites, this.proxyWhites);
        }
        return true;
    }

    @Override
    public final boolean isAllInProxyWhites(Iterable<String> remotes) {
        if(this.enabledProxyWhites()) {
            return private_checkIpIsAllIn(remotes, this.allowAllOfProxyWhites, this.proxyWhites);
        }
        return true;
    }

    @Override
    public final boolean isAnyInProxyWhites(String... remotes) {
        if(this.enabledProxyWhites()) {
            return private_checkIpIsAnyIn(remotes, this.allowAllOfProxyWhites, this.proxyWhites);
        }
        return true;
    }

    @Override
    public final boolean isAnyInProxyWhites(Iterable<String> remotes) {
        if(this.enabledProxyWhites()) {
            return private_checkIpIsAnyIn(remotes, this.allowAllOfProxyWhites, this.proxyWhites);
        }
        return true;
    }

    @Override
    public AbsGatewayManager addInIpWhites(String clientIP) {
        if(this.enabledIpWhites()) {
            if("*".equals(clientIP)) this.allowAllOfIpWhites = true;
            else this.ipWhites.insert(clientIP);
        }
        return this;
    }

    @Override
    public AbsGatewayManager addAllInIpWhites(String... clientIPs) {
        if(this.enabledIpWhites()) {
            if(protected_changeAll(true, clientIPs) != null) this.allowAllOfIpWhites = true;
            this.ipWhites.insertBatch(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager addAllInIpWhites(Iterable<String> clientIPs) {
        if(this.enabledIpWhites())  {
            if(protected_changeAll(true, clientIPs) != null) this.allowAllOfIpWhites = true;
            this.ipWhites.insertBatch(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeFromIpWhites(String clientIP) {
        if(this.enabledIpWhites()) {
            if("*".equals(clientIP)) this.allowAllOfIpWhites = false;
            else this.ipWhites.remove(clientIP);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeAllFromIpWhites(String... clientIPs) {
        if(this.enabledIpWhites()) {
            if(protected_changeAll(false, clientIPs) != null) this.allowAllOfIpWhites = false;
            this.ipWhites.removeAll(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeAllFromIpWhites(Iterable<String> clientIPs) {
        if(this.enabledIpWhites()) {
            if(protected_changeAll(false, clientIPs) != null) this.allowAllOfIpWhites = false;
            this.ipWhites.removeAll(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager clearIpWhites() {
        if(this.enabledIpWhites()) {
            this.ipWhites.clear();
            this.allowAllOfIpWhites = false;
        }
        return this;
    }

    @Override
    public final boolean isInIpWhites(String clientIP) {
        if(this.enabledIpWhites()) {
            return private_checkIpIsIn(clientIP, this.allowAllOfIpWhites, this.ipWhites);
        }
        return true;
    }

    @Override
    public final boolean isAllInIpWhites(String... clientIPs) {
        if(this.enabledIpWhites()) {
            return private_checkIpIsAllIn(clientIPs, this.allowAllOfIpWhites, this.ipWhites);
        }
        return true;
    }

    @Override
    public final boolean isAllInIpWhites(Iterable<String> clientIPs) {
        if(this.enabledIpWhites()) {
            return private_checkIpIsAllIn(clientIPs, this.allowAllOfIpWhites, this.ipWhites);
        }
        return true;
    }

    @Override
    public final boolean isAnyInIpWhites(String... clientIPs) {
        if(this.enabledIpWhites()) {
            return private_checkIpIsAnyIn(clientIPs, this.allowAllOfIpWhites, this.ipWhites);
        }
        return true;
    }

    @Override
    public final boolean isAnyInIpWhites(Iterable<String> clientIPs) {
        if(this.enabledIpWhites()) {
            return private_checkIpIsAnyIn(clientIPs, this.allowAllOfIpWhites, this.ipWhites);
        }
        return true;
    }

    @Override
    public AbsGatewayManager addInIpBlacks(String clientIP) {
        if(this.enabledIpBlacks()) {
            if("*".equals(clientIP)) this.denyAllOfIpBlacks = true;
            else this.ipBlacks.insert(clientIP);
        }
        return this;
    }

    @Override
    public AbsGatewayManager addInIpBlacks(String... clientIPs) {
        if(this.enabledIpBlacks()) {
            if(protected_changeAll(true, clientIPs) != null) this.denyAllOfIpBlacks = true;
            this.ipBlacks.insertBatch(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager addInIpBlacks(Iterable<String> clientIPs) {
        if(this.enabledIpBlacks()) {
            if(protected_changeAll(true, clientIPs) != null) this.denyAllOfIpBlacks = true;
            this.ipBlacks.insertBatch(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeFromIpBlacks(String clientIP) {
        if(this.enabledIpBlacks()) {
            if("*".equals(clientIP)) this.denyAllOfIpBlacks = false;
            else this.ipBlacks.remove(clientIP);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeAllFromIpBlacks(String... clientIPs) {
        if(this.enabledIpBlacks()) {
            if(protected_changeAll(false, clientIPs) != null) this.denyAllOfIpBlacks = false;
            this.ipBlacks.removeAll(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager removeAllFromIpBlacks(Iterable<String> clientIPs) {
        if(this.enabledIpBlacks()) {
            if(protected_changeAll(false, clientIPs) != null) this.denyAllOfIpBlacks = false;
            this.ipBlacks.removeAll(clientIPs);
        }
        return this;
    }

    @Override
    public AbsGatewayManager clearIpBlacks() {
        if(this.enabledIpBlacks()) {
            this.ipBlacks.clear();
            this.denyAllOfIpBlacks = false;
        }
        return this;
    }

    @Override
    public final boolean isInIpBlacks(String clientIP) {
        if(this.enabledIpBlacks()) {
            return private_checkIpIsIn(clientIP, this.denyAllOfIpBlacks, this.ipBlacks);
        }
        return false;
    }

    @Override
    public final boolean isAllInIpBlacks(String... clientIPs) {
        if(this.enabledIpBlacks()) {
            return private_checkIpIsAllIn(clientIPs, this.denyAllOfIpBlacks, this.ipBlacks);
        }
        return false;
    }

    @Override
    public final boolean isAllInIpBlacks(Iterable<String> clientIPs) {
        if(this.enabledIpBlacks()) {
            return private_checkIpIsAllIn(clientIPs, this.denyAllOfIpBlacks, this.ipBlacks);
        }
        return false;
    }

    @Override
    public final boolean isAnyInIpBlacks(String... clientIPs) {
        if(this.enabledIpBlacks()) {
            return private_checkIpIsAnyIn(clientIPs, this.denyAllOfIpBlacks, this.ipBlacks);
        }
        return false;
    }

    @Override
    public final boolean isAnyInIpBlacks(Iterable<String> clientIPs) {
        if(this.enabledIpBlacks()) {
            return private_checkIpIsAnyIn(clientIPs, this.denyAllOfIpBlacks, this.ipBlacks);
        }
        return false;
    }

    /* ********************************************************************************************
     *
     *         核心逻辑
     *
     * *********************************************************************************************
     * */

    @Override
    public final void init(FilterConfig filterConfig) throws ServletException {
        this.protected_getLogger().info("网关过滤器 {} 已启动并初始化完成", Integer.toHexString(this.hashCode()));
    }

    @Override
    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //先跨域过滤
        if(this.corsFilter != null) {
            boolean b = this.corsFilter.preFiltering(request, response);
            if(b) return;
        }
        Logger logger = this.protected_getLogger();
        response.setContentType(this.responseContentType);
        response.setCharacterEncoding(this.responseCharset);
        //尝试获取代理请求头
        String proxyHeader = WebKit.getProxyHeader(request);
        String clientIP = WebKit.buildClientIPByHeader(proxyHeader, request);
        //检查是否开启了 “直连 IP 检查” 功能，是则检验是否允许访问。否则直接放行
        //同时添加当前客户端 IP 到 HttpServletRequest 域内
        if(this.private_checkIsDenyAccessByProxyWhite(logger, request, response, proxyHeader, clientIP)) {
            return;
        }
        //判断 “REST 接口禁用列表” 功能是否开启，若开启则判断当前 REST 接口 URI 是否位于列表中
        //是则拒绝访问，返回 HTTP 403 错误
        if(this.enabledDisabledURIs()) {
            String requestURI = request.getRequestURI();
            if(this.protected_isInDisabledURIs(requestURI)) {
                logger.error("REST 接口 【{}】 已被禁用，拒绝本次访问", requestURI);
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
        }
        //判断 “客户端 IP 访问白名单” 功能是否开启，若开启则判断当前客户端 IP 是否位于名单中
        //不在则拒绝访问，返回 HTTP 403 错误
        if(this.enabledIpWhites()) {
            if(!private_checkIpIsIn(clientIP, this.allowAllOfIpWhites, this.ipWhites)) {
                logger.error("当前客户端 IP 【{}】 没有位于访问白名单内，拒绝本次访问", clientIP);
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                return;
            }
        }
        //判断 “客户端 IP 访问黑名单” 功能是否开启，若开启则判断当前客户端 IP 是否位于名单中
        //若位于其中则拒绝访问，返回 HTTP 403 错误
        //若不在黑名单中，则判断是否开启了拉黑功能；若开启了，则判断当前客户端 IP 的访问次数是否已经达到了允许的最大值
        //若达到了则封禁此客户端 IP
        if(this.enabledIpBlacks()) {
            if(private_checkIpIsIn(clientIP, this.denyAllOfIpBlacks, this.ipBlacks)) {
                logger.error("是否拒绝全部请求: {}, 当前客户端 IP 【{}】 位于访问黑名单内，拒绝本次访问", 
                        this.denyAllOfIpBlacks, clientIP);
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                return;
            }
            if(this.enabledAddBlack) {
                String timeunitLabel = this.protected_checkIfAddIPInBlacksAndGetTimeunitLabel(clientIP);
                if(timeunitLabel != null) {
                    if(this.authenticationManager != null) {
                        this.authenticationManager.handleDisable(request);
                    }
                    logger.error("客户端 IP 【{}】 在 {} {}内访问超过阈值 {} 次，已被加入 “客户端 IP 访问黑名单” 内",
                            clientIP, addBlackerThreshold, timeunitLabel, addBlackerThreshold);
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    return;
                }
            }
        }
        //放行请求
        filterChain.doFilter(request, response);
    }

    @Override
    public final void destroy() {
        this.protected_getLogger().info("网关过滤器 {} 已销毁完毕!", Integer.toHexString(this.hashCode()));
    }


    /* ********************************************************************************************
     *
     *         私有逻辑
     *
     * *********************************************************************************************
     * */

    @Note("输出开启功能日志")
    private static void private_printEnabledLog(Logger logger, Object obj, String message) {
        if(obj != null) logger.info(message);
    }

    @Note("初始化 “直连 IP 列表” 到缓存内")
    private void private_initProxyWhites(Logger logger, GatewaySource source) {
        if(this.enabledProxyWhites()) {
            Set<String> proxyWhites = source.getProxyWhites();
            this.proxyWhites = private_buildCIDRPrefixTree(proxyWhites);
            int size = proxyWhites.size();
            logger.info("开启 “直连 IP 校验” 功能，只允许特定的 “IP/主机” 访问该应用程序。当前列表的 “IP/主机” 个数: {}", size);
        }
    }

    @Note("初始化 “已禁用的 REST 接口” 到缓存内")
    private void private_initDisabledURIs(Logger logger, GatewaySource source) {
        if(this.enabledDisabledURIs()) {
            Set<String> proxyWhites = source.getDisableURIs();
            String contextPath = source.getServerContextPath();
            this.serverContextPath = (contextPath == null || contextPath.isEmpty())
                    ? ProfileKit.getServerContextPath()
                    : contextPath;
            int size = this.protected_initDisabledURIs(proxyWhites);
            logger.info("开启 “Rest 接口禁用列表” 功能，访问位于列表中的 REST 接口会返回 HTTP 404 错误。当前列表的 “禁用 REST 接口” 个数: {}", size);
        }
    }

    @Note("初始化 “客户端 IP 访问白名单” 到缓存内")
    private void private_initIpWhites(Logger logger, GatewaySource source) {
        if(this.enabledIpWhites()) {
            Set<String> ipWhites = source.getIpWhites();
            this.ipWhites = private_buildCIDRPrefixTree(ipWhites);
            int size = ipWhites.size();
            logger.info("开启 “客户端 IP 访问白名单” 功能，位于该白名单中的 “IP/主机” 才能访问该应用程序，否则返回 HTTP 403 错误。当前访问白名单的客户端 IP 个数: {}", size);
        }
    }

    @Note("初始化 “客户端 IP 访问黑名单” 到缓存内")
    private void private_initIpBlacks(Logger logger, GatewaySource source) {
        if(this.enabledIpBlacks()) {
            Set<String> ipBlacks = source.getIpBlacks();
            this.ipBlacks = private_buildCIDRPrefixTree(ipBlacks);
            int size = ipBlacks.size();
            logger.info("开启 “客户端 IP 访问黑名单” 功能，位于该黑名单中的 “IP/主机” 将被拒绝访问该应用程序，返回 HTTP 403 错误。当前访问黑名单的客户端 IP 个数: {}", size);
        }
    }


    @Note("获取客户端 IP 信息且判断与校验是否开启 “直连 IP 校验” 功能，开了就验证其是否允许被放行")
    private boolean private_checkIsDenyAccessByProxyWhite(Logger logger, HttpServletRequest request,
                                                          HttpServletResponse response,
                                                          String proxyHeader, String clientIP) {
        //尝试获取来自客户端的公网 IP
        String numberIP = request.getHeader("Application-Time");
        String requestTime = request.getHeader("Request-Time");
        String publicNetIP = null;
        if(VerifyKit.isAllInteger(numberIP, requestTime)) {
            long numIP = Long.parseLong(numberIP) -  Long.parseLong(requestTime);
            publicNetIP = SysKit.toStringIp((int) numIP);
        }
        //如果开启了 “直连 IP 校验” 功能
        if(this.enabledProxyWhites()) {
            //那就必须要携带代理头，没有那就拒绝访问
            if(proxyHeader == null) {
                logger.error("“直连 IP 校验” 功能开启后，网关过滤器强制要求的 Header 没有携带");
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return true;
            }
            String remoteAddr = request.getRemoteAddr();
            if(!private_checkIpIsIn(remoteAddr, this.allowAllOfProxyWhites, this.proxyWhites)) {
                logger.error("主机 IP remote address 【{}】 不在 “允许直连的 IP 白名单” 内，拒绝本次访问", remoteAddr);
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return true;
            }
        }
        //获取代理头携带的公网 IP。与获取到的公网 IP 比较，不一致则采用代理头的作为客户端的真实 IP
        private_setIPAttributes(publicNetIP, clientIP, request);
        return false;
    }



    @Note("设置 publicNetIP 和 clientIP 到 HttpServletRequest 域内")
    private static void private_setIPAttributes(String publicNetIP, String clientIP,
                                                HttpServletRequest request) {
        String clientIPName = WebKit.CLIENT_IP_NAME;
        String publicNetIpName = WebKit.PUBLIC_NET_IP_NAME;
        if(publicNetIP == null) {
            //客户端 IP 就采用本机，公网 IP 为空
            request.setAttribute(clientIPName, clientIP);
            request.setAttribute(publicNetIpName, "");
        } else {
            //客户端 IP 跟公网 IP一致。
            request.setAttribute(clientIPName, publicNetIP);
            request.setAttribute(publicNetIpName, publicNetIP);
        }
    }


    @Note("拿到 IP Set 集合生成 CIDRPrefixTree 实例")
    private static CIDRPrefixTree private_buildCIDRPrefixTree(Set<String> ipSet) {
        if(ipSet != null && !ipSet.isEmpty()) {
            Set<String> temp = new HashSet<>((int) ((ipSet.size() / 0.75) + 1));
            for(String ip : ipSet) {
                ip = private_checkAndGetIP(ip);
                if(ip != null) temp.add(ip);
            }
            return new CIDRPrefixTree(temp);
        }
        return new CIDRPrefixTree();
    }

    @Note("校验 IP")
    private static String private_checkAndGetIP(String ip) {
        if(ip != null && !ip.isEmpty()) {
            if(ip.contains("/")) return ip;
            return ip + "/32";
        }
        return null;
    }
    
    @Note("校验是否允许通过")
    private static boolean private_checkIpIsIn(String ip, boolean all, CIDRPrefixTree cidrPrefixTree) {
        if(all) return true;
        return cidrPrefixTree.contains(ip);
    }

    @Note("校验是否全部允许通过")
    private static boolean private_checkIpIsAllIn(String[] ips, boolean all, CIDRPrefixTree cidrPrefixTree) {
        if(all) return true;
        return cidrPrefixTree.containsAll(ips);
    }

    @Note("校验是否全部允许通过")
    private static boolean private_checkIpIsAllIn(Iterable<String> ips, boolean all, CIDRPrefixTree cidrPrefixTree) {
        if(all) return true;
        return cidrPrefixTree.containsAll(ips);
    }

    @Note("校验有一个通过就返回 true")
    private static boolean private_checkIpIsAnyIn(String[] ips, boolean all, CIDRPrefixTree cidrPrefixTree) {
        if(all) return true;
        return cidrPrefixTree.containsAny(ips);
    }

    @Note("校验有一个通过就返回 true")
    private static boolean private_checkIpIsAnyIn(Iterable<String> ips, boolean all, CIDRPrefixTree cidrPrefixTree) {
        if(all) return true;
        return cidrPrefixTree.containsAny(ips);
    }

    @Note("如果有 * 通配符就返回 newValue，否则就返回 null")
    protected static Boolean protected_changeAll(boolean newValue, String[] values) {
        if(values == null || values.length == 0) return null;
        for(String ip : values) {
            if("*".equals(ip)) return newValue;
        }
        return null;
    }

    @Note("如果有 * 通配符就返回 newValue，否则就返回 null")
    protected static Boolean protected_changeAll(boolean newValue, Iterable<String> values) {
        if(protected_iterableIsEmpty(values)) return null;
        for(String ip : values) {
            if("*".equals(ip)) return newValue;
        }
        return null;
    }


    protected static boolean protected_iterableIsEmpty(Iterable<String> values) {
        return RowKit.getIterableSize(values) == 0;
    }

    protected final String[] protected_checkAsNewURISet(String[] uris, Boolean allValue) {
        if(uris == null) return null;
        int length = uris.length;
        if(length == 0) return null;
        String[] newArray = new String[length];
        for (int i = 0; i < length; i++) {
            String uri = uris[i];
            if("*".equals(uri) && allValue != null) this.protected_setDisabledAllURIProperty(allValue);
            newArray[i] = WebKit.uriAddServerContextPath(uri, this.serverContextPath);
        }
        return newArray;
    }

    protected final String[] protected_checkAsNewURISet(Iterable<String> uris, Boolean allValue) {
        int size = RowKit.getIterableSize(uris);
        if(size == 0) return null;
        String[] newArray = new String[size];
        int index = 0;
        for (String uri : uris) {
            if("*".equals(uri) && allValue != null) this.protected_setDisabledAllURIProperty(allValue);
            newArray[index] = WebKit.uriAddServerContextPath(uri, this.serverContextPath);
        }
        return newArray;
    }
}
